package com.example.annolog.utils;

import com.example.annolog.constants.CommonErrorNoConstants;
import com.example.annolog.exception.BaseException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Arrays;
import java.util.List;

/**
 * ReflectUtils
 * @author administrator
 * @date 2019-12-12
 */
@Slf4j
public class ReflectUtils {

    final static Logger logger= LoggerFactory.getLogger(ReflectUtils.class);


    /**
             * 根据给定类型获取成员对象数组
     * 
     * @param clazz
     *            class类型
     * @return 包括从父类继承的成员对象
     */
    public static Field[] getFieldArray(Class<?> clazz)
    {
        Field[] fields = clazz.getDeclaredFields();
        if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
            fields = ArrayUtils.addAll(fields, getFieldArray(clazz.getSuperclass()));
        }
        return fields;
    }
    
    /**
             * 根据给定类型获取成员对象集合
     * 
     * @param clazz
     *            class类型
     * @return 包括从父类继承的成员对象
     */
    public static List<Field> getFieldList(Class<?> clazz)
    {
        return Arrays.asList(getFieldArray(clazz));
    }
    
    /**
             * 根据给定类型获取成员方法数组
     * 
     * @param clazz
     *            class类型
     * @return 包括从父类继承的成员方法
     */
    public static Method[] getMethodArray(Class<?> clazz)
    {
        Method[] methods = clazz.getDeclaredMethods();
        if (clazz.getSuperclass() != null && clazz.getSuperclass() != Object.class) {
            methods = ArrayUtils.addAll(methods, getMethodArray(clazz.getSuperclass()));
        }
        return methods;
    }
    
    /**
             * 根据给定类型获取成员方法集合
     * 
     * @param clazz
     *            class类型
     * @return 包括从父类继承的成员方法
     */
    public static List<Method> getMethodList(Class<?> clazz)
    {
        return Arrays.asList(getMethodArray(clazz));
    }
    
    /**
             * 获取泛型字段的实际类型数组.
     * 
     * @param field
             *            字段对象
     * @param index
             *            类型数组中目标类型下标
     * @return
     */
    public static Class<?> getActualType(Field field, int index) throws BaseException {
        return (Class<?>) getActualType(field.getGenericType())[index];
    }
    
    /**
             * 获取泛型返回值方法的的实际类型.
     * 
     * @param method
             *            方法对象
     * @param index
             *            类型数组中目标类型下标
     * @return
     */
    public static Class<?> getActualReturnType(Method method, int index) throws BaseException {
        return (Class<?>) getActualType(method.getGenericReturnType())[index];
    }
    
    /**
             * 获取泛型实体（类、接口、基本类型或 void）的直接<code>父类<code>的实际类型数组.
     * 
     * @param cls
     *            class类型
     * @param index
             *            类型数组中目标类型下标
     * @return
     */
    public static Class<?> getActualType(Class<?> cls, int index) throws BaseException {
        return (Class<?>) getActualType(cls.getGenericSuperclass())[index];
    }
    
    /**
             * 获取泛型实体（类、接口、基本类型或 void）的直接<code>接口<code>的实际类型数组.
     * 
     * @param cls
     *            class类型
     * @param interfaceIndex
             *            接口数组中下标
     * @param index
             *            类型数组中目标类型下标
     * @return
     */
    public static Class<?> getActualType(Class<?> cls, int interfaceIndex, int index) throws BaseException {
        return (Class<?>) getActualType(cls.getGenericInterfaces()[interfaceIndex])[index];
    }
    
    /**
             * 获取泛型类型的实际类型数组.
     * 
     * @param type
             *            类型
     * @return
     */
    private static Type[] getActualType(Type type) throws BaseException {
        if (type instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) type;
            return pt.getActualTypeArguments();
        }
        else {
            String msg = StringUtils.formatStr("不支持的类型:{0}", type.getClass().getName());
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
        }
    }
    
    /**
             * 直接读取对象属性值, 忽略private/protected修饰符, 不经过getter函数.
     *
     * @param object
             *            目标对象
     * @param fieldName
             *            属性名称
     * @return Object 属性值
     * @throws
             *
     */
    public static Object getFieldValue(final Object object, final String fieldName) throws BaseException
    {
        Field field;
        try {
            field = getDeclaredField(object.getClass(), fieldName);
        }
        catch (Exception e1) {
            String msg = StringUtils.formatStr("Could not find field [{0}] on target [{1}]", fieldName, object);
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
        }
        
        makeAccessible(field);
        Object result = "";
        try {
            result = field.get(object);
        }
        catch (IllegalAccessException e) {
            throw new BaseException(CommonErrorNoConstants.ERR_COMMON, "工具类执行异常");
        }
        return result;
    }
    
    /**
             * 循环向上转型,获取对象的DeclaredField.
     *
     * @param clazz
             *            目标类
     * @param propertyName
             *            属性名称
     * @return Field 字段对象
     * @throws
             *
     */
    public static Field getDeclaredField(Class<?> clazz, String propertyName) throws BaseException
    {
        for (Class<?> superClass = clazz; superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(propertyName);
            }
            catch (NoSuchFieldException e) {
                // Field不在当前类定义,继续向上转型
                logger.warn("code={},msg={}", CommonErrorNoConstants.ERR_COMMON, "获取对象属性异常", e);
            }
        }
        String msg = StringUtils.formatStr("No such field: {0}'.'{1}", clazz.getName(), propertyName);
        throw new BaseException(CommonErrorNoConstants.ERR_COMMON, msg);
    }
    
    /**
         * 强行设置Field可访问.
     *
     * @param field
             *            目标字段
     */
    private static void makeAccessible(final Field field)
    {
        if (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) {
            field.setAccessible(true);
        }
    }
    
    /**
             * 给对象里面的指定属性赋值
     *
     * @param obj
             *            对象
     * @param fieldName
             *            字段名称
     * @param value
             *            字段值
     * @throws Exception
             *             异常
     */
    public static void setValueByFieldName(Object obj, String fieldName, Object value) throws Exception
    {
        Field field = getFieldByFieldName(obj, fieldName);
        if (field.isAccessible()) {
            field.set(obj, value);
        }
        else {
            field.setAccessible(true);
            field.set(obj, value);
            field.setAccessible(false);
        }
    }
    
    /**
             * 获取对象中指定属性的值
     *
     * @param obj
             *            对象
     * @param fieldName
             *            字段名称
     * @return
     */
    @SuppressWarnings("rawtypes")
    public static Field getFieldByFieldName(Object obj, String fieldName)
    {
        for (Class superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
            try {
                return superClass.getDeclaredField(fieldName);
            }
            catch (NoSuchFieldException e) {
                logger.warn("code={},msg={}", CommonErrorNoConstants.ERR_COMMON, "获取对象属性异常", e);
            }
        }
        return null;
    }

    public static <T extends Annotation> T getAnnotationFromInvocation(Method targetMethod, Class<T> annotationClass) throws Exception {
       Class<?>[] par=targetMethod.getParameterTypes();
       String  name=targetMethod.getName();
        Class<?>[] interfaces=targetMethod.getDeclaringClass().getInterfaces();
        for (Class<?> oneInterface: interfaces){
            try {
                Method method=oneInterface.getMethod(name,par);
                if (method!=null){
                    if (method.isAnnotationPresent(annotationClass)){
                        return method.getAnnotation(annotationClass);
                    }
                }
            }catch (NoSuchMethodException e){

            }

        }
        return null;
    }


    public static <T extends Annotation> T getMethodAnnotation(Method targetMethod, Class<T> annotationClass) throws Exception {
        return targetMethod.getAnnotation(annotationClass);
    }

    public static <T extends Annotation> T getClassAnnotation(Class <?> targetClass, Class<T> annotationClass) throws Exception {
        return targetClass.getAnnotation(annotationClass);
    }

}
